#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <array>
#include <math.h>
#include <exception>
#include <fstream>

#include "config.h"
#include "katana.h"
#include "stl.h"
#define pi 3.1415926


// load an ASCII .stl file
// fill the vertices and triangle list. the vertices are unified while loading.
//void STLReader::loadStl(const char* filename, std::vector<Vertex>& vertices, std::vector<Triangle>& triangles)
void STLReader::loadStl(const char* filename)
{
    // as .stl stores unconnected triangles, any vertex found is usually repeated in
    // several more triangles. to remesh that heap of triangles, we unify those vertices.
   // printf("Loading %s...\n", filename);
    FILE* file = fopen(filename, "r");
    char line[256];
    // collection of unique vertices and their index number
    std::map<Vertex, int> uniqueVertices;

    // collection of vertex indicies to reconstruct triangles after vertex merging
    std::vector<int> indices;
    // collection of triangle normals
    std::vector<Vertex> normals;
    while (!feof(file)) {
        // read file line by line
        if (fgets(line, sizeof(line), file)) {
            Vertex p, n;
            // we scan for vertex definitions, their triangle linking is given by
            // groups of three consecutive definitions.
            DEBUG("Scanning: " << line);

            // scan for triangle normal definition
            int found = sscanf(line, " facet normal %e %e %e", &n.x, &n.y, &n.z);
            if (found)
                normals.push_back(n);     // store triangle normal

              // scan for vertex definition
            found = sscanf(line, " vertex %e %e %e", &p.x, &p.y, &p.z);
            if (found) {
                // we found a vertex declaration
                // check if this vertex is already known
                int index;
                if (uniqueVertices.count(p) == 1) {
                    // we know the vertex, so get its index
                    index = uniqueVertices[p];
                    DEBUG("Found duplicate(" << index << ") vertex: " << p.x << ", " << p.y << ", " << p.z);
                }
                else {
                    // this is a new vertex, so store it
                    //std::cout << Katana::Instance().vertices.size() << std::endl;
                    //std::cout << p.x << " " << p.y << " " << p.z << std::endl;
                    Katana::Instance().vertices.push_back(p);
                    //printf("===\n");
                    index = Katana::Instance().vertices.size() - 1; // the new vertex is the last element
                    //printf("+++\n");
                    uniqueVertices[p] = index; // store index
                    DEBUG("Found " << index << " vertex: " << p.x << ", " << p.y << ", " << p.z);
                }
                indices.push_back(index); // store index in triangle order
            }
        }
    }
    fclose(file);
    //std::cout << indices.size() << " " << normals.size() << std::endl;
    DEBUG("Indices: " << indices.size() << " normals: " << normals.size());

    // if we read triangles only, there are triangles*3 vertex indices
    assert(indices.size() % 3 == 0);
    assert(indices.size() == normals.size() * 3);

    // create triangles
    for (unsigned int i = 0; i < indices.size(); i += 3)
    {
        Triangle t;

        // store vertex pointers
        for (int j = 0; j < 3; j++)
            t.vertices[j] = &Katana::Instance().vertices[indices[i + j]];
        // sort vertices bottom-up for later operations
        t.sortTriangleVertices();
        // store normal
        t.normal = normals[i / 3];
        // add triangle
        Katana::Instance().triangles.push_back(t);
        f_triangles.push_back(t);
    }
    //saveStl("haha.stl", Katana::Instance().triangles);
    //printf("Loading complete: %u vertices read, %u unique, %u triangles\n", (int)indices.size(), (int)Katana::Instance().vertices.size(), (int)Katana::Instance().triangles.size());
}

void STLReader::saveStl(std::string filename, const std::vector<Triangle>& triangles)
{
    std::ofstream dstream(filename);
    if (!dstream.is_open()) {
        std::cout << "can not open " << filename << std::endl;
        return;
    }
    dstream << "solid STL generated by MeshLab" << std::endl;
    for (int i = 0; i < triangles.size(); i++) {
        dstream << "  facet normal " << triangles[i].normal.x << " " << triangles[i].normal.y << " " << triangles[i].normal.z << std::endl;
        dstream << "    outer loop" << std::endl;
        for (int j = 0; j < 3; j++) {
            dstream << "      vertex  " << triangles[i]._vertices[j].x << " " << triangles[i]._vertices[j].y << " " << triangles[i]._vertices[j].z << std::endl;
        }
        dstream << "    endloop" << std::endl;
        dstream << "  endfacet" << std::endl;
    }
    dstream << "endsolid vcg" << std::endl;
    dstream.close();
}

void STLReader::getMultiDirectionStl(const char* filename)
{

    std::vector<Vertex>   _vertices;
    std::vector<Triangle> _triangles;

    // collection of unique vertices and their index number
    std::map<Vertex, int> _uniqueVertices;

    // collection of vertex indicies to reconstruct triangles after vertex merging
    std::vector<int> _indices;
    // collection of triangle normals
    std::vector<Vertex> _normals;

    FILE* file = fopen(filename, "r");
    char line[256];

    while (!feof(file)) {
        // read file line by line
        if (fgets(line, sizeof(line), file)) {
            Vertex p, n;
            // we scan for vertex definitions, their triangle linking is given by
            // groups of three consecutive definitions.
            DEBUG("Scanning: " << line);

            // scan for triangle normal definition
            int found = sscanf(line, " facet normal %e %e %e", &n.x, &n.y, &n.z);
            if (found)
                _normals.push_back(n);     // store triangle normal

              // scan for vertex definition
            found = sscanf(line, " vertex %e %e %e", &p.x, &p.y, &p.z);
            if (found) {
                // we found a vertex declaration
                // check if this vertex is already known
                int index;
                if (_uniqueVertices.count(p) == 1) {
                    // we know the vertex, so get its index
                    index = _uniqueVertices[p];
                    DEBUG("Found duplicate(" << index << ") vertex: " << p.x << ", " << p.y << ", " << p.z);
                }
                else {
                    // this is a new vertex, so store it
                    _vertices.push_back(p);
                    index = _vertices.size() - 1; // the new vertex is the last element
                    _uniqueVertices[p] = index; // store index
                    DEBUG("Found " << index << " vertex: " << p.x << ", " << p.y << ", " << p.z);
                }
                _indices.push_back(index); // store index in triangle order
            }
        }
    }
    fclose(file);

    DEBUG("Indices: " << _indices.size() << " normals: " << _normals.size());

    // if we read triangles only, there are triangles*3 vertex indices
    assert(_indices.size() % 3 == 0);
    assert(_indices.size() == _normals.size() * 3);

    // create triangles
    for (unsigned int i = 0; i < _indices.size(); i += 3)
    {
        Triangle t;

        // store vertex pointers
        for (int j = 0; j < 3; j++)
            t._vertices[j] = _vertices[_indices[i + j]];
        // sort vertices bottom-up for later operations
        //t.sortTriangleVertices();
        // store normal
        t.normal = _normals[i / 3];
        // add triangle
        _triangles.push_back(t);
    }
    printf("Loading complete: %u vertices read, %u unique, %u triangles\n", (int)_indices.size(), (int)_vertices.size(), (int)_triangles.size());

    //Get many printing direction stl
    std::vector<Triangle> r_triangles;
    std::string _filename(filename);
    std::string derectory = "../model/diff_dir/" + file_name;
    _mkdir(derectory.c_str());

    int num = 0;
    saveStl("../model/diff_dir/" + file_name + "/" + std::to_string(num) + suffix_stl, _triangles);
    num++;

    SAMPLE_ON_BALL sampling;
    sampling.GenerateRandomPoints();

    for (int i = 0; i < sampling.random_points.size(); i++) {
        r_triangles.clear();
        Eigen::Vector3d _before(_normals[0].x, _normals[0].y, _normals[0].z);
        Eigen::Vector3d _after(sampling.random_points[i].x, sampling.random_points[i].y, sampling.random_points[i].z);
        Eigen::MatrixXd rotate_matrix = rot(_before, _after);
        for (unsigned int i = 0; i < _indices.size(); i += 3) {
            Triangle t;
            for (int j = 0; j < 3; j++) {
                Eigen::Vector3d p(_vertices[_indices[i + j]].x, _vertices[_indices[i + j]].y, _vertices[_indices[i + j]].z);
                Eigen::Vector3d rotate_p = rotate_matrix * p;
                t._vertices[j] = Vertex(rotate_p.x(), rotate_p.y(), rotate_p.z());
            }
            Eigen::Vector3d normal(_normals[i / 3].x, _normals[i / 3].y, _normals[i / 3].z);
            Eigen::Vector3d rotate_normal = rotate_matrix * normal;
            t.normal = Vertex(rotate_normal.x(), rotate_normal.y(), rotate_normal.z());
            r_triangles.push_back(t);
        }
        saveStl("../model/diff_dir/" + file_name + "/" + std::to_string(num) + suffix_stl, r_triangles);
        num++;
    }
    /*for (int rx = 0; rx <= direction_samplings; rx++) {
        for (int ry = 0; ry <= direction_samplings; ry++) {
                r_triangles.clear();
                for (unsigned int i = 0; i < _indices.size(); i += 3) {
                    Triangle t;
                    double angle_x = rx * (360.0 / direction_samplings) * pi / 180.0;
                    double angle_y = ry * (360.0 / direction_samplings) * pi / 180.0;
                    for (int j = 0; j < 3; j++) {
                        Vertex p = _vertices[_indices[i + j]];
                        p = p.Rotate(angle_x, Vertex(1, 0, 0), p);
                        p = p.Rotate(angle_y, Vertex(0, 0, 1), p);
                        t._vertices[j] = p;
                    }
                    Vertex normal = _normals[i / 3];
                    normal = normal.Rotate(angle_x, Vertex(1, 0, 0), normal);
                    normal = normal.Rotate(angle_y, Vertex(0, 0, 1), normal);
                    t.normal = normal;
                    r_triangles.push_back(t);
                }
                saveStl("../model/diff_dir/" + file_name + "/" + std::to_string(num) + suffix_stl , r_triangles);
                num++;
        }
    }*/
}



